// baltzo_zoneinfo.h                                                  -*-C++-*-
#ifndef INCLUDED_BALTZO_ZONEINFO
#define INCLUDED_BALTZO_ZONEINFO

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a value type to represent a time zone.
//
//@CLASSES:
//  baltzo::Zoneinfo: information about a time zone
//  baltzo::ZoneinfoTransition: attributes representing a time transition
//
//@SEE_ALSO: baltzo_localtimedescriptor, baltzo_zoneinfoutil
//
//@DESCRIPTION: This component provides a *value* *semantic* type,
// `baltzo::Zoneinfo`, that represents the information about a specific time
// zone contained in the Zoneinfo database.  In addition, this component
// provides an unconstrained *in-core* *value-semantic* type
// `baltzo::ZoneinfoTransition` that can be used to characterize a transition
// for time zones.
//
///`baltzo::ZoneinfoTransition`
///----------------------------
// A `baltzo::ZoneinfoTransition` contains:
// ```
// Name        Type
// ----------  --------------------------
// utcTime     bdlt::EpochUtil::TimeT64
// descriptor  baltzo::LocalTimeDescriptor
// ```
// * `utcTime`: UTC time when a transition occurs
// * `descriptor`: local time value corresponding to the time transition
//
// For example, in New York on March 14, 2011, at the instant 1 a.m., clocks
// are set forward by an hour to mark the transition from Eastern Standard Time
// to Eastern Daylight Time.  This change can be represented by a
// `baltzo::ZoneinfoTransition` object whose `utcTime` refers to March 14,
// 2011, 1am and whose `descriptor` describes Eastern Daylight Time (i.e.,
// description is "EDT", `dstInEffectFlag` is `true`, and `utcOffsetInSeconds`
// is -14,400 (-4 * 60 * 60)).
//
///`baltzo::Zoneinfo`
///------------------
// A `baltzo::Zoneinfo` contains:
//
// * the time zone identifier (e.g., "America/New_York" or "Asia/Tokyo")
// * the ordered sequence of `baltzo::ZoneinfoTransition` objects,
//   representing the various transitions from UTC for this time zone.
// * an optional POSIX-like TZ environment string used to represent
//   far-reaching times past the end of the explicit time zone data.
//
// A `baltzo::Zoneinfo` object also provides the method
// `findTransitionForUtcTime` that allows a client to find, in the sequence of
// transitions, the appropriate transition whose local-time descriptor
// describes the properties of local time, at a specified UTC time, for the
// time zone in question.  Note that, even though this information is
// sufficient for converting local date and time, to their corresponding values
// in other time zones, clients are encouraged to use the utilities provided in
// `baltzo_timezoneutil`.  Also note that, `baltzo::Zoneinfo` objects are
// typically populated by the client through the `baltzo::Loader` protocol, and
// not directly.
//
///Zoneinfo Database
///-----------------
// This database, also referred to as either the TZ database or the Olson
// database (after its creator, Arthur Olson), is a standard, public-domain
// time-zone information distribution used by many software systems (including
// a number of Unix variants and the Java Runtime Environment).  Information
// about the Zoneinfo database -- including the time zone rules for the
// supported time zones, and source code for the `zic` compiler (for compiling
// those rules into the binary representation used by this component) -- can be
// found online at `http://www.iana.org/time-zones/repository/tz-link.html`.
// This time zone information can be used to perform the conversion of dates
// and times from UTC to their corresponding dates and times in a given time
// zone and vice-versa.  (See `baltzo_zoneinfobinaryreader` for more
// information about the binary file format.)
//
///posixExtendedRangeDescription
///-----------------------------
// This string may be populated with a POSIX-like TZ string that describes
// rules for local time are handled before the first and after the last
// local-time transitions maintained by this object.  Typically this is used
// for computing local time values far in the future.  The rules for the
// encoded string can be found online at
// `http://www.ibm.com/developerworks/aix/library/au-aix-posix/`.
//
///Usage
///-----
// The following usage examples illustrate how to populate a `baltzo::Zoneinfo`
// object and use it to transform a UTC time into a local time.
//
///Example 1: Populate a `baltzo::Zoneinfo`
/// - - - - - - - - - - - - - - - - - - - -
// Suppose we want to represent the time-zone information for New York, in 2010
// using a `baltzo::Zoneinfo` object.  In order to do so, we need to provide
// the UTC date-times (transitions) after which the time zone changes its
// offset from UTC, or daylight-saving Time starts or ends.
//
// First, we define two times "Mar 14, 2010 6:00 AM" and "Nov 07, 2010 7:00 AM"
// representing respectively the UTC time at which New York transitions to
// Eastern Daylight-saving Time (EDT) and Eastern Standard Time (EST) in 2010:
// ```
// bdlt::Datetime edtDatetime(2010, 03, 14, 2, 0);
// bdlt::Datetime estDatetime(2010, 11, 07, 2, 0);
// ```
// Then, we create two local-time descriptors that hold the offset from UTC and
// DST status for EST and EDT in New York in 2010, in terms of their
// `offsetFromUtcInSeconds`, `dstInEffectFlag` and `description` attributes:
// ```
// const baltzo::LocalTimeDescriptor est(-5 * 60 * 60, false, "EST");
// const baltzo::LocalTimeDescriptor edt(-4 * 60 * 60, true,  "EDT");
// ```
// Note that these descriptors will be associated with the created transition
// times, to reflect how local time in New York changes its offset from UTC and
// DST status after specific times.
//
// Next, we create an empty `baltzo::Zoneinfo` object that will be populated
// with the information necessary to describe the time zone for New York:
// ```
// baltzo::Zoneinfo newYorkTimeZone;
// ```
// Then, before being able to associate the transition times with their
// corresponding descriptors, we need translate the transition times to
// `bdlt::EpochUtil::TimeT64`:
// ```
// bdlt::EpochUtil::TimeT64 edtTransitionTime =
//                             bdlt::EpochUtil::convertToTimeT64(edtDatetime);
// bdlt::EpochUtil::TimeT64 estTransitionTime =
//                             bdlt::EpochUtil::convertToTimeT64(estDatetime);
// ```
// Now, we associate the created descriptors with the transitions we indicated
// previously and add them to `newYorkTimeZone` using the `addTransition`
// method:
// ```
// newYorkTimeZone.addTransition(edtTransitionTime, edt);
// newYorkTimeZone.addTransition(estTransitionTime, est);
// ```
// Note that this insertion operation maintains the transitions in order of
// transition time, and therefore inserting transitions out-of-order, while not
// illegal, is very inefficient.
//
// Finally we verify that the `newYorkTimeZone` contains the transitions we
// indicated:
// ```
// {
//     baltzo::Zoneinfo::TransitionConstIterator it =
//                                         newYorkTimeZone.beginTransitions();
//
//     assert(it->utcTime()    == edtTransitionTime);
//     assert(it->descriptor() == edt);
//     ++it;
//     assert(it->utcTime()    == estTransitionTime);
//     assert(it->descriptor() == est);
// }
// ```
// Notice that the iterator refers to a `baltzo::ZoneinfoTransition` object.
//
///Example 2: Converting UTC to Local Time
///- - - - - - - - - - - - - - - - - - - -
// Suppose that we want to program a function, `utcToLocalTime` to perform the
// conversion from a UTC time value to the corresponding local time value, in a
// given time zone, and return the computed local time:
//
// First we declare the function `utcToLocalTime` and its contract:
// ```
// /// Return the `bdlt::DatetimeTz` value representing the date, time and
// /// offset from UTC (rounded to the minute) value of the local time,
// /// corresponding to the specified `utcTime` in the specified
// /// `timeZone`.  The behavior is undefined if the `utcTime` precedes the
// /// time of the first transition contained in `timeZone` and
// /// `0 < timeZone.numTransitions()`.
// static bdlt::DatetimeTz utcToLocalTime(const bdlt::Datetime&   utcTime,
//                                       const baltzo::Zoneinfo& timeZone)
// {
// ```
// Then, we check the precondition of `utcToLocalTime`, by checking that
// `timeZone` contains at least one transitions and comparing `utcTime` to the
// first transition time in `timeZone`:
// ```
//     BSLS_ASSERT(0 < timeZone.numTransitions());
//     BSLS_ASSERT(timeZone.firstTransition().utcTime() <=
//                                bdlt::EpochUtil::convertToTimeT64(utcTime));
// ```
// Next, we obtain the appropriate `baltzo::ZoneinfoTransition` object,
// invoking the method `findTransitionForUtcTime` on `timeZone`:
// ```
//     baltzo::Zoneinfo::TransitionConstIterator it =
//                                 timeZone.findTransitionForUtcTime(utcTime);
// ```
// Then, we access the descriptor associated with the transition to which `it`
// refers, and calculate the offset from UTC rounded to the minute:
// ```
//     const baltzo::ZoneinfoTransition& transition = *it;
//     const int offsetInMinutes =
//                          transition.descriptor().utcOffsetInSeconds() / 60;
// ```
// Now, we apply the obtained `offsetInMinutes` to the originally specified
// `utcTime` obtaining the corresponding local time in the specified
// `timeZone`:
// ```
//     bdlt::Datetime temp(utcTime);
//     temp.addMinutes(offsetInMinutes);
// ```
// Finally, return the local time value together with its offset from UTC:
// ```
//     return bdlt::DatetimeTz(temp, offsetInMinutes);
// }
// ```
// Suppose, now, we want to convert UTC time to the corresponding local time in
// New York.  We can do so using the previously defined function
// `utcToLocalTime` and reusing the `baltzo::Zoneinfo` object,
// `newYorkTimeZone` of Example 1.
//
// First, we define `bdlt::Datetime` object representing the UTC time "Apr 10,
// 2010 12:00":
// ```
// bdlt::Datetime utcDatetime(2010, 04, 10, 12, 0, 0);
// ```
// Then, we invoke `utcToLocalTime` passing `newYorkTimeZone` as a time zone
// and save the result:
// ```
// bdlt::DatetimeTz nyDatetime = utcToLocalTime(utcDatetime, newYorkTimeZone);
// ```
// Finally, we compute the New York local time corresponding to `utcDatetime`,
// verify that "April 10, 2010 8:00" is the computed time:
// ```
// const bdlt::Datetime expectedTime(2010, 4, 10, 8, 0, 0);
// assert(-4 * 60      == nyDatetime.offset());
// assert(expectedTime == nyDatetime.localDatetime());
// ```

#include <balscm_version.h>

#include <baltzo_localtimedescriptor.h>

#include <bdlt_datetime.h>
#include <bdlt_epochutil.h>

#include <bslalg_swaputil.h>

#include <bslma_allocator.h>
#include <bslma_bslallocator.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_isbitwisemoveable.h>
#include <bslmf_movableref.h>
#include <bslmf_nestedtraitdeclaration.h>

#include <bsls_assert.h>
#include <bsls_keyword.h>
#include <bsls_review.h>
#include <bsls_types.h>

#include <bsl_cstring.h>
#include <bsl_iosfwd.h>
#include <bsl_set.h>
#include <bsl_string.h>
#include <bsl_vector.h>

namespace BloombergLP {
namespace baltzo {

                          // ========================
                          // class ZoneinfoTransition
                          // ========================

/// This class is an unconstrained *in-core* value-semantic class that
/// characterizes a transition when the local time value of a time-zone
/// changes.  The salient attributes of this type are the `utcTime`
/// (representing seconds from UTC), and `descriptor` representing the local
/// time value after the transition.
class ZoneinfoTransition {

    // DATA
    bdlt::EpochUtil::TimeT64   d_utcTime;       // UTC time (representing in
                                                // seconds from epoch) when the
                                                // time transition occurs

    const LocalTimeDescriptor *d_descriptor_p;  // pointer to the descriptor
                                                // associated with this
                                                // transition (held, not owned)

    // FRIENDS
    friend class Zoneinfo;

    // PRIVATE CREATORS

    /// Create a `ZoneinfoTransition` object having the specified `utcTime`
    /// and, `descriptor` attribute values.  The behavior is undefined
    /// unless `descriptor` remains valid for the lifetime of this object.
    ZoneinfoTransition(bdlt::EpochUtil::TimeT64   utcTime,
                       const LocalTimeDescriptor *descriptor);

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(ZoneinfoTransition,
                                   bslmf::IsBitwiseMoveable);

    // CREATORS

    /// Destroy this object.
    ~ZoneinfoTransition();

    // ACCESSORS

    /// Return a reference providing non-modifiable access to the
    /// `descriptor` attribute of this object.
    const LocalTimeDescriptor& descriptor() const;

    /// Return the value of the `utcTime` attribute of this object.
    bdlt::EpochUtil::TimeT64 utcTime() const;

                                // Aspects

    /// Write the value of this object to the specified output `stream` in a
    /// human-readable format, and return a reference to `stream`.
    /// Optionally specify an initial indentation `level`, whose absolute
    /// value is incremented recursively for nested objects.  If `level` is
    /// specified, optionally specify `spacesPerLevel`, whose absolute value
    /// indicates the number of spaces per indentation level for this and
    /// all of its nested objects.  If `level` is negative, suppress
    /// indentation of the first line.  If `spacesPerLevel` is negative,
    /// format the entire output on one line, suppressing all but the
    /// initial indentation (as governed by `level`).  If `stream` is not
    /// valid on entry, this operation has no effect.  Note that the format
    /// is not fully specified, and can change without notice.
    bsl::ostream& print(bsl::ostream& stream,
                        int           level = 0,
                        int           spacesPerLevel = 4) const;
};

// FREE OPERATORS

/// Return `true` if the specified `lhs` and `rhs` objects have the same
/// value, and `false` otherwise.  Two `ZoneinfoTransition` objects have the
/// same value if the corresponding value of their `utcTime` attribute is
/// the same and both refer to the same `descriptor` address.
bool operator==(const ZoneinfoTransition& lhs, const ZoneinfoTransition& rhs);

/// Return `true` if the specified `lhs` and `rhs` objects do not have the
/// same value, and `false` otherwise.  Two `ZoneinfoTransition` objects do
/// not have the same value if the corresponding value of their `utcTime` is
/// not the same or if they do not refer to the same `descriptor` address.
bool operator!=(const ZoneinfoTransition& lhs, const ZoneinfoTransition& rhs);

/// Return `true` if the value of the specified `lhs` is less than (ordered
/// before) the value of the specified `rhs`.  Note that the value of `lhs`
/// is less than the value of `rhs` if the value of the `utcTime` attribute
/// of `lhs` is less than the value of the `utcTime` attribute of `rhs`.
bool operator<(const ZoneinfoTransition& lhs, const ZoneinfoTransition& rhs);

/// Write the value of the specified `object` to the specified output
/// `stream` in a single-line format, and return a reference to `stream`.
/// If `stream` is not valid on entry, this operation has no effect.  Note
/// that this human-readable format is not fully specified and can change
/// without notice.  Also note that this method has the same behavior as
/// `object.print(stream, 0, -1)` with the attribute names elided.
bsl::ostream& operator<<(bsl::ostream&             stream,
                         const ZoneinfoTransition& object);

                               // ==============
                               // class Zoneinfo
                               // ==============

/// This class is a value-semantic type holding a structured representation
/// of the information contained in an Zoneinfo (or "Olson") time zone
/// database for a *single* locale (e.g., "America/New_York").  The salient
/// attributes of this type are the string identifier and the ordered
/// sequence of `ZoneinfoTransition` objects.
class Zoneinfo {

    // PRIVATE TYPES

    /// This `class` is a private functor that provides a comparator
    /// predicate for the type `LocalTimeDescriptor`, so that it can be
    /// stored in associative containers such as `bsl::set`.
    class DescriptorLess {

      public:
        /// Return `true` if the value of the specified `lhs` is less than
        /// (ordered before) the value of the specified `rhs`.  Note that
        /// the value of `lhs` is less than the value of `rhs` if the value
        /// of the corresponding `utcOffsetInSeconds`, `description`, and
        /// `dstInEffectFlag` attributes of `lhs` when incrementally
        /// compared one at a time in that order is less than the attribute
        /// value of `rhs`.  Also note that the comparison moves to the next
        /// attribute only when the corresponding attribute values compare
        /// equal.
        bool operator()(const LocalTimeDescriptor& lhs,
                        const LocalTimeDescriptor& rhs) const;
    };

    /// Alias for the sequence of transitions that characterize a `Zoneinfo`
    /// object.
    typedef bsl::vector<ZoneinfoTransition> TransitionSequence;

    /// Alias for the set of unique local-time descriptors that are managed
    /// by a `Zoneinfo` object.
    typedef bsl::set<LocalTimeDescriptor, DescriptorLess> DescriptorSet;

    // DATA
    bsl::string         d_identifier;
                          // this time zone's id

    DescriptorSet       d_descriptors;
                          // set of local time descriptors for this time zone
                          // (e.g., 'EST')

    TransitionSequence  d_transitions;
                          // transitions, from one local time descriptor to
                          // another (e.g., 'EST' to 'EDT'), ordered by the
                          // time the transition occurred (or will occur)

    bsl::string         d_posixExtendedRangeDescription;
                          // optional POSIX-like TZ environment string
                          // representing far-reaching times

    // FRIENDS
    friend bool operator==(const Zoneinfo&, const Zoneinfo&);

  public:
    // TYPES
    typedef bsl::allocator<char> allocator_type;

    /// Alias for a bi-directional `const` iterator over the sequence of
    /// transitions maintained by a `Zoneinfo` object.
    typedef TransitionSequence::const_iterator TransitionConstIterator;

    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(Zoneinfo, bslma::UsesBslmaAllocator);

    // CREATORS

    /// Create a `Zoneinfo` object having the values:
    /// ```
    /// numTransitions() == 0
    /// identifier()     == ""
    /// ```
    /// Optionally specify an `allocator` (e.g., the address of a
    /// `bslma::Allocator` object) to supply memory; otherwise, the default
    /// allocator is used.
    Zoneinfo();
    explicit Zoneinfo(const allocator_type& allocator);

    /// Create a `Zoneinfo` object having the same value as the specified
    /// `original` object.  Optionally specify an `allocator` (e.g., the
    /// address of a `bslma::Allocator` object) to supply memory; otherwise,
    /// the default allocator is used.
    Zoneinfo(const Zoneinfo&       original,
             const allocator_type& allocator = allocator_type());

    /// Create a `Zoneinfo` object having the same value and the same
    /// allocator as the specified `original` object.  The value of
    /// `original` becomes unspecified but valid, and its allocator remains
    /// unchanged.
    Zoneinfo(bslmf::MovableRef<Zoneinfo> original) BSLS_KEYWORD_NOEXCEPT;

    /// Create a `Zoneinfo` object having the same value as the specified
    /// `original` object, using the specified `allocator` (e.g., the
    /// address of a `bslma::Allocator` object) to supply memory.  The
    /// allocator of `original` remains unchanged.  If `original` and the
    /// newly created object have the same allocator then the value of
    /// `original` becomes unspecified but valid, and no exceptions will be
    /// thrown; otherwise `original` is unchanged and an exception may be
    /// thrown.
    Zoneinfo(bslmf::MovableRef<Zoneinfo> original,
             const allocator_type&       allocator);

    // MANIPULATORS

    /// Assign to this object the value of the specified `rhs` object, and
    /// return a reference providing modifiable access to this object.
    Zoneinfo& operator=(const Zoneinfo& rhs);

    /// Assign to this object the value of the specified `rhs` object, and
    /// return a non-`const` reference to this object.  The allocators of
    /// this object and `rhs` both remain unchanged.  If `rhs` and this
    /// object have the same allocator then the value of `rhs` becomes
    /// unspecified but valid, and no exceptions will be thrown; otherwise
    /// `rhs` is unchanged (and an exception may be thrown).
    Zoneinfo& operator=(bslmf::MovableRef<Zoneinfo> rhs);

    /// Add to this object a transition occurring at the specified `utcTime`
    /// when the local time in the described time-zone adopts the
    /// characteristics of the specified `descriptor`.  If a transition at
    /// `utcTime` is already present, replace it's local-time descriptor
    /// with `descriptor`.
    void addTransition(bdlt::EpochUtil::TimeT64   utcTime,
                       const LocalTimeDescriptor& descriptor);

    /// Set the `identifier` attribute of this object to the specified
    /// `value`.
    void setIdentifier(const bsl::string_view&  value);
    void setIdentifier(const char              *value);

    /// Set the `posixExtendedRangeDescription` attribute of this object,
    /// used to describe local time transitions far in the future, to the
    /// specified `value` (see {posixExtendedRangeDescription}).
    void setPosixExtendedRangeDescription(const bsl::string_view&  value);
    void setPosixExtendedRangeDescription(const char              *value);

    /// Efficiently exchange the value of this object with the value of the
    /// specified `other` object.  This method provides the no-throw
    /// exception-safety guarantee.  The behavior is undefined unless this
    /// object was created with the same allocator as `other`.
    void swap(Zoneinfo& other);

    // ACCESSORS

    /// Return the allocator used by this object to supply memory.  Note
    /// that if no allocator was supplied at construction the currently
    /// installed default allocator is used.
    ///
    /// @DEPRECATED: Use `get_allocator()` instead.
    bslma::Allocator *allocator() const;

    /// Return an iterator providing non-modifiable access to the transition
    /// that holds the local-time descriptor associated with the specified
    /// `utcTime`.  The behavior is undefined unless `numTransitions() > 0`
    /// and `utcTime` is at or after the transition returned by
    /// `firstTransition`.
    TransitionConstIterator findTransitionForUtcTime(
                                          const bdlt::Datetime& utcTime) const;

    /// Return a reference providing non-modifiable access to the first
    /// transition contained in this object.  The behavior is undefined
    /// unless `numTransitions() > 0`.
    const ZoneinfoTransition& firstTransition() const;

    /// Return the allocator used by this object to supply memory.  Note
    /// that if no allocator was supplied at construction the default
    /// allocator in effect at construction is used.
    allocator_type get_allocator() const;

    /// Return a reference providing non-modifiable access to the
    /// `identifier` attribute of this object.
    const bsl::string& identifier() const;

    /// Return a reference providing non-modifiable access to the
    /// `posixExtendedRangeDescription` attribute of this object, used to
    /// describe local time transitions far in the future (see
    /// {posixExtendedRangeDescription}).
    const bsl::string& posixExtendedRangeDescription() const;

    /// Return the number of transitions maintained by this zone info.
    bsl::size_t numTransitions() const;

    /// Return an iterator providing non-modifiable access to the first
    /// transition in the ordered sequence of transitions maintained by this
    /// object.  Note that if `beginTransitions() == endTransitions()` then
    /// there are no transitions stored by this object.
    TransitionConstIterator beginTransitions() const;

    /// Return an iterator providing non-modifiable access to the one-past
    /// the last transition in the ordered sequence of transitions that is
    /// associated with this object.  Note that if
    /// `beginTransitions() == endTransitions()` then there are no
    /// transitions stored by this object.
    TransitionConstIterator endTransitions() const;

    /// Write the value of this object to the specified output `stream` in a
    /// human-readable format, and return a reference to `stream`.
    /// Optionally specify an initial indentation `level`, whose absolute
    /// value is incremented recursively for nested objects.  If `level` is
    /// specified, optionally specify `spacesPerLevel`, whose absolute value
    /// indicates the number of spaces per indentation level for this and
    /// all of its nested objects.  If `level` is negative, suppress
    /// indentation of the first line.  If `spacesPerLevel` is negative,
    /// format the entire output on one line, suppressing all but the
    /// initial indentation (as governed by `level`).  If `stream` is not
    /// valid on entry, this operation has no effect.  Note that the format
    /// is not fully specified, and can change without notice.
    bsl::ostream& print(bsl::ostream& stream,
                        int           level = 0,
                        int           spacesPerLevel = 4) const;
};

// FREE OPERATORS

/// Return `true` if the specified `lhs` and `rhs` objects have the same
/// value, and `false` otherwise.  Two `Zoneinfo` objects have the same
/// value if the corresponding value of their `identifier` attribute is the
/// same and if both store the same sequence of transitions, ordered by
/// time.
bool operator==(const Zoneinfo& lhs, const Zoneinfo& rhs);

/// Return `true` if the specified `lhs` and `rhs` objects do not have the
/// same value, and `false` otherwise.  Two `Zoneinfo` objects do not have
/// the same value if their corresponding `identifier` attribute does not
/// have the same value, or if both do *not* store the same sequence of
/// transitions, ordered by time.
bool operator!=(const Zoneinfo& lhs, const Zoneinfo& rhs);

/// Write the value of the specified `object` to the specified output
/// `stream` in a single-line format, and return a reference to `stream`.
/// If `stream` is not valid on entry, this operation has no effect.  Note
/// that this human-readable format is not fully specified, can change
/// without notice, and is logically equivalent to:
/// ```
/// print(stream, 0, -1);
/// ```
bsl::ostream& operator<<(bsl::ostream& stream, const Zoneinfo& object);

// FREE FUNCTIONS

/// Exchange the values of the specified `a` and `b` objects.  This function
/// provides the no-throw exception-safety guarantee if the two objects were
/// created with the same allocator and the basic guarantee otherwise.
void swap(Zoneinfo& a, Zoneinfo& b);

// ============================================================================
//                            INLINE DEFINITIONS
// ============================================================================

                          // ------------------------
                          // class ZoneinfoTransition
                          // ------------------------

// PRIVATE CREATORS
inline
ZoneinfoTransition::ZoneinfoTransition(bdlt::EpochUtil::TimeT64   utcTime,
                                       const LocalTimeDescriptor *descriptor)
: d_utcTime(utcTime)
, d_descriptor_p(descriptor)
{
    BSLS_ASSERT(descriptor);
}

// CREATORS
inline
ZoneinfoTransition::~ZoneinfoTransition()
{
    BSLS_ASSERT(d_descriptor_p);
}

// ACCESSORS
inline
const LocalTimeDescriptor& ZoneinfoTransition::descriptor() const
{
    return *d_descriptor_p;
}

inline
bdlt::EpochUtil::TimeT64 ZoneinfoTransition::utcTime() const
{
    return d_utcTime;
}

}  // close package namespace

// FREE OPERATORS
inline
bool baltzo::operator==(const ZoneinfoTransition& lhs,
                        const ZoneinfoTransition& rhs)
{
    return  lhs.utcTime()    == rhs.utcTime()
         && lhs.descriptor() == rhs.descriptor();
}

inline
bool baltzo::operator!=(const ZoneinfoTransition& lhs,
                        const ZoneinfoTransition& rhs)
{
    return  !(lhs == rhs);
}

inline
bool baltzo::operator<(const ZoneinfoTransition& lhs,
                       const ZoneinfoTransition& rhs)
{
    return lhs.utcTime() < rhs.utcTime();
}

namespace baltzo {

                               // --------------
                               // class Zoneinfo
                               // --------------

// CREATORS
inline
Zoneinfo::Zoneinfo()
: d_identifier()
, d_descriptors()
, d_transitions()
, d_posixExtendedRangeDescription()
{
}

inline
Zoneinfo::Zoneinfo(const allocator_type& allocator)
: d_identifier(allocator)
, d_descriptors(allocator)
, d_transitions(allocator)
, d_posixExtendedRangeDescription(allocator)
{
}

// MANIPULATORS
inline
Zoneinfo& Zoneinfo::operator=(const Zoneinfo& rhs)
{
    Zoneinfo(rhs, get_allocator()).swap(*this);
    return *this;
}

inline
void Zoneinfo::setIdentifier(const bsl::string_view& value)
{
    BSLS_ASSERT(0 != value.data());

    d_identifier = value;
}

inline
void Zoneinfo::setIdentifier(const char *value)
{
    BSLS_ASSERT(value);

    d_identifier = value;
}

inline
void Zoneinfo::setPosixExtendedRangeDescription(const bsl::string_view& value)
{
    BSLS_ASSERT(0 != value.data());

    d_posixExtendedRangeDescription.assign(value.begin(), value.end());
}

inline
void Zoneinfo::setPosixExtendedRangeDescription(const char *value)
{
    BSLS_ASSERT(value);

    d_posixExtendedRangeDescription = value;
}

inline
void Zoneinfo::swap(Zoneinfo& other)
{
    BSLS_ASSERT(get_allocator() == other.get_allocator());

    bslalg::SwapUtil::swap(&d_identifier,  &other.d_identifier);
    bslalg::SwapUtil::swap(&d_descriptors, &other.d_descriptors);
    bslalg::SwapUtil::swap(&d_transitions, &other.d_transitions);
    bslalg::SwapUtil::swap(&d_posixExtendedRangeDescription,
                           &other.d_posixExtendedRangeDescription);
}

// ACCESSORS
inline
bslma::Allocator *Zoneinfo::allocator() const
{
    return get_allocator().mechanism();
}

inline
const ZoneinfoTransition& Zoneinfo::firstTransition() const
{
    BSLS_ASSERT(numTransitions() > 0);

    return d_transitions.front();
}

inline
Zoneinfo::allocator_type Zoneinfo::get_allocator() const
{
    return d_identifier.get_allocator();
}

inline
const bsl::string& Zoneinfo::identifier() const
{
    return d_identifier;
}

inline
const bsl::string& Zoneinfo::posixExtendedRangeDescription() const
{
    return d_posixExtendedRangeDescription;
}

inline
bsl::size_t Zoneinfo::numTransitions() const
{
    return d_transitions.size();
}

inline
Zoneinfo::TransitionConstIterator Zoneinfo::beginTransitions() const
{
    return d_transitions.begin();
}

inline
Zoneinfo::TransitionConstIterator Zoneinfo::endTransitions() const
{
    return d_transitions.end();
}

}  // close package namespace

// FREE OPERATORS
inline
bool baltzo::operator==(const Zoneinfo& lhs, const Zoneinfo& rhs)
{
    return lhs.identifier() == rhs.identifier()
        && lhs.posixExtendedRangeDescription() ==
                                            rhs.posixExtendedRangeDescription()
        && lhs.numTransitions() == rhs.numTransitions()
        && bsl::equal(lhs.d_transitions.begin(),
                      lhs.d_transitions.end(),
                      rhs.d_transitions.begin());
}

inline
bool baltzo::operator!=(const Zoneinfo& lhs, const Zoneinfo& rhs)
{
    return !(lhs == rhs);
}

}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2020 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
